package com.iqik.sc.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@RequestMapping("/autodeliver")
public class AutoDeliverController {

    @Autowired
    private RestTemplate restTemplate;

    /***
     * 改造前
     * @param userId
     * @return
     */
    @GetMapping("/checkState/{userId}")
    public Integer findResumeOpenState(@PathVariable Long userId) {
        //  添加riboon负载均衡策略之后， 应用失效！！！
        return restTemplate.getForObject("http://localhost:8080/resume/openstate/" + userId, Integer.class);
    }

    @Autowired
    private DiscoveryClient discoveryClient;

    /**
     * 改造第一版
     */
    @GetMapping("/checkState/v1/{userId}")
    public Integer findResumeOpenStateV1(@PathVariable Long userId) {
        // 从Eureka Server中获取我们关注的服务实例信息以及接口， 添加riboon负载均衡策略之后， 应用失效！！！
        // 1。从Eureka server中获取service-resume服务的实例信息（列表），使用客户端
        List<ServiceInstance> instances = discoveryClient.getInstances("service-resume");
        // 2。在列表中选择使用其中一个（负载均衡）
        ServiceInstance serviceInstance = instances.get(0);
        // 3.从元数据信息中获取信息 host port
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        String url = String.format("http://%s:%d/resume/openstate/%d", host, port, userId);

        return restTemplate.getForObject(url, Integer.class);
    }

    /**
     * 改造第二版 负载均衡 使用ribbon
     */
    @GetMapping("/checkState/v2")
    public Integer portV2() {
        // 使用ribbon不需要获取服务实例列表然后选择其中之一发送请求, 添加riboon负载均衡策略之后，原有的和改造第一版就不能用啦！！！
        String url = "http://service-resume/resume/port";
        return restTemplate.getForObject(url, Integer.class);
    }

    @GetMapping("/checkState/v2/{userId}")
    public Integer findResumeOpenStateV2(@PathVariable Long userId) {
        // 使用ribbon不需要获取服务实例列表然后选择其中之一发送请求, 添加riboon负载均衡策略之后，原有的和改造第一版就不能用啦！！！
        String url = "http://service-resume/resume/openstate/" + userId;
        return restTemplate.getForObject(url, Integer.class);
    }

    /**
     * 改造第三版，使用hystrix熔断器
     * 提供者模拟处理超时
     * 使用@HystrixCommand进行熔断控制
     */
    @HystrixCommand(
            // 线程池标识
            threadPoolKey = "findResumeOpenStateV3",
            // 线程池属性配置
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value="1"), // 线程数
                    @HystrixProperty(name = "maxQueueSize", value = "20")
            },
            commandProperties = {
                    // 每个属性都是一个HystrixProperty
                    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "2000")
            }
    )
    @GetMapping("/checkState/v3/{userId}")
    public Integer findResumeOpenStateV3(@PathVariable Long userId) {
        String url = "http://service-resume/resume/openstate/" + userId;
        return restTemplate.getForObject(url, Integer.class);
    }

    /**
     * 1.服务端提供者处理超时，熔断，返回错误信息，
     * 2.有可能服务提供者出现异常直接抛出异常信息
     *
     *   以上信息都会返回到消费者端，很多时候消费者不希望把接收到的异常/错误信息再抛到它的上游
     */

    // 定义回退方法，返回预设默认值，该方法和返回值与原始方法保持一致
    public Integer v3Fallback(Long userId) {
        return -1;
    }

    @HystrixCommand(
            // 线程池标识
            threadPoolKey = "findResumeOpenStateV3Fallback",
            // 线程池属性配置
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value="2"), // 线程数
                    @HystrixProperty(name = "maxQueueSize", value = "20")
            },
            commandProperties = {
                    // 每个属性都是一个HystrixProperty
                    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "2000")
            }, fallbackMethod = "v3Fallback" // 回退方法
    )
    @GetMapping("/checkState/v3/fallback/{userId}")
    public Integer findResumeOpenStateV3Fallback(@PathVariable Long userId) {
        String url = "http://service-resume/resume/openstate/" + userId;
        return restTemplate.getForObject(url, Integer.class);
    }

    @HystrixCommand(
            // 线程池标识
            threadPoolKey = "findResumeOpenStateV3Status",
            // 线程池属性配置
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value="2"), // 线程数
                    @HystrixProperty(name = "maxQueueSize", value = "20")
            },
            commandProperties = {
                    // 每个属性都是一个HystrixProperty
                    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "2000"),

                    // 定制工作细节
                    // 时间窗口定义 ms
                    @HystrixProperty(name="metrics.rollingStats.timeInMilliseconds", value = "8000"),
                    // 统计时间窗口内最小请求数
                    @HystrixProperty(name="circuitBreaker.requestVolumeThreshold", value = "2"),
                    // 统计时间窗口内错误百分比阈值
                    @HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value = "50"),
                    // 自我修复时的活动窗口长度
                    @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds", value = "3000")
            }, fallbackMethod = "v3Fallback" // 回退方法
    )
    @GetMapping("/checkState/v3/status/{userId}")
    public Integer findResumeOpenStateV3Status(@PathVariable Long userId) {
        String url = "http://service-resume/resume/openstate/" + userId;
        return restTemplate.getForObject(url, Integer.class);
    }

}
